-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Pushing changes in progress for git-lfs support adding to entire ecosystem. #9766
Pushing changes in progress for git-lfs support adding to entire ecosystem. #9766
Conversation
…-core-for-lfs-as-an-option-for-repositories
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added some inline suggestions and here's some general Ruby feedback. I've added links to the docs for our linter, RuboCop, which provide some examples and more detail. Not trying to nitpick, just trying to save you some headache with the linter and code reviews 🙂
- Naming conventions: method names, method parameter names, block parameter names, and variable names use
snake_case
. - Redundant
return
. In Ruby the last expression in a block or method is the return value of that block or method. In these cases the explicitreturn
should be omitted. It is common/idiomatic to use guard clauses in methods to reduce if/else nesting, so thereturn
is obviously needed for those cases. unless !!value
is unnecessary. Just let the value evaluate as truthy/falsy. Hint: in Rubyfalse
andnil
are falsy and literally all other values are truthy. If you need to explicitly check fornil
, usevalue.nil?
.- You probably haven't gotten to this point in the implementation yet, but if you end up adding methods to a file that has the
# typed: strict
annotation you're going to want to add Sorbetsig
blocks to the methods so the type checker passes. The syntax is weird so I would just look for other examples in the file/codebase and follow what they're doing.
lfsEnabled = true | ||
commandString = getCommandString(path,lfsEnabled) | ||
/debugger | ||
p commandString/ | ||
SharedHelpers.run_shell_command(commandString | ||
).split("\n").filter_map do |line| |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this could be simplified a bit without the helper methods and by using Dir.chdir
lfsEnabled = true | |
commandString = getCommandString(path,lfsEnabled) | |
/debugger | |
p commandString/ | |
SharedHelpers.run_shell_command(commandString | |
).split("\n").filter_map do |line| | |
lfs_enabled = # ... | |
git_bin = lfs_enabled ? "git-lfs" : "git" | |
Dir.chdir(path) do | |
SharedHelpers.run_shell_command("#{git_bin} ls-files --stage") | |
end.split("\n").filter_map do |line| |
#the HEREDOC command will see any stray spaces. | ||
return "git -C #{path} ls-files --stage" unless lfsEnabled | ||
Dependabot.logger.warn("LFS is enabled in this repo. Please use an LFS enabled client") | ||
commandString = "CWD=\"#{__dir__}\";cd #{path};git-lfs ls-files --stage;cd $CWD" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggested something a little simpler above, but just adding a couple notes:
__dir__
is the name of the directory that contains this source code file, not the current working directory of the process- Rather than setting an environment variable for the directory, this is a good use case for
Dir.chdir
. This method changes into the given directory, runs whatever is inside block, and then changes back to the original directory.
With some discussion, I have a path towards getting this back on track.
if lfs detected, use git-lfs |
fed7b21
to
e610181
Compare
…fs-as-an-option-for-repositories' of https://github.com/dependabot/dependabot-core into GarryHurleyJr/6039-add-support-in-dependabot-core-for-lfs-as-an-option-for-repositories
@@ -890,10 +890,10 @@ def find_submodules(path) | |||
sig { params(path: String).returns(T::Boolean) } | |||
def isLfsEnabled(path) | |||
filepath = File.join(path,".gitattributes") | |||
lfsEnabled = File.exist?(filepath) && File.readable?(filepath) && SharedHelpers.run_shell_command("cat #{filepath} | grep \"filter=lfs\"").include? "#{filepath}" | |||
lfsEnabled = T.let(true, T::Boolean) if File.exist?(filepath) && File.readable?(filepath) && SharedHelpers.run_shell_command("cat #{filepath} | grep \"filter=lfs\"").include? "filter=lfs" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
any reason for copying the same code pattern in 3 different files? Can we centralize it in one location?
@@ -304,7 +304,7 @@ def fetch_files | |||
before do | |||
allow(file_fetcher_instance).to receive(:commit).and_return("sha") | |||
end | |||
#start of lfs testing | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we have unit tests covering these new scenarios?
rescue Exception => e | ||
# this should not be needed, but I don't trust 'should' | ||
lfs_enabled = T.let(false, T::Boolean) | ||
raise e.exception("Message: #{e.message}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're rescuing the exception, setting lfs_enabled
and then immediately re-raising the exception. What's the point of doing all of that? The exception will halt the code from executing, so all of this can just be removed and the end result is the same: an exception is raised.
git -C #{path} ls-files --stage | ||
CMD | ||
).split("\n").filter_map do |line| | ||
lfs_enabled = is_lfs_enabled(path) if lfs_enabled.nil? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lfs_enabled = is_lfs_enabled(path) if lfs_enabled.nil? | |
lfs_enabled = is_lfs_enabled(path) if lfs_enabled.nil? |
Looks like we're calling .nil?
on lfs_enabled
before it is defined, what exactly are we trying to do here?
sig { params(path: String).returns(T.nilable(T::Boolean)) } | ||
def is_lfs_enabled(path) | ||
filepath = File.join(path, ".gitattributes") | ||
lfs_enabled = T.let(true, T::Boolean) if File.exist?(filepath) && File.readable?(filepath) && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lfs_enabled = T.let(true, T::Boolean) if File.exist?(filepath) && File.readable?(filepath) && | |
T.let(true, T::Boolean) if File.exist?(filepath) && File.readable?(filepath) && |
No need to assign anything to this variable, we're just returning the results of the method calls
sig { params(path: String).returns(T.nilable(T::Boolean)) } | ||
def is_lfs_enabled(path) | ||
filepath = File.join(path, ".gitattributes") | ||
lfs_enabled = T.let(true, T::Boolean) if File.exist?(filepath) && File.readable?(filepath) && | ||
SharedHelpers.run_shell_command("cat #{filepath} | grep \"filter=lfs\"").include?("filter=lfs") | ||
rescue Exception => e | ||
# this should not be needed, but I don't trust 'should' | ||
lfs_enabled = T.let(false, T::Boolean) | ||
raise e.exception("Message: #{e.message}") | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think all of this can be simplified to this:
sig { params(path: String).returns(T.nilable(T::Boolean)) } | |
def is_lfs_enabled(path) | |
filepath = File.join(path, ".gitattributes") | |
lfs_enabled = T.let(true, T::Boolean) if File.exist?(filepath) && File.readable?(filepath) && | |
SharedHelpers.run_shell_command("cat #{filepath} | grep \"filter=lfs\"").include?("filter=lfs") | |
rescue Exception => e | |
# this should not be needed, but I don't trust 'should' | |
lfs_enabled = T.let(false, T::Boolean) | |
raise e.exception("Message: #{e.message}") | |
end | |
sig { params(path: String).returns(T::Boolean) } | |
def lfs_enabled?(path) | |
gitattributes_path = File.join(path, '.gitattributes') | |
return false unless File.exist?(gitattributes_path) | |
File.readlines(gitattributes_path).each do |line| | |
return true if line =~ /filter=lfs/ | |
end | |
false | |
end |
Dir.chdir(path) do | ||
run_shell_command("git reset HEAD --hard") | ||
run_shell_command("git clean -fx") | ||
if lfs_enabled?(path) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need 4 different methods in 3 different places to check if lfs is enabled?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because we have three different files in common that each call the git command in a different way and none of them refer to the others. To make matters worse, some code never even bothers to use any of them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like the could all still share a single, shared way to check if lfs is enabled?
T.let(true, T::Boolean) if File.exist?(filepath) && File.readable?(filepath) && | ||
SharedHelpers.run_shell_command("cat #{filepath} | grep \"filter=lfs\"") | ||
.include?("filter=lfs") | ||
rescue StandardError => e |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Jurre's suggestion is more idiomatic and won't incur the cost of shelling out, so I think you should incorporate that.
Just to call attention to it, shelling out to grep like this will return a non-zero exit if there's no match. SharedHelpers.run_shell_command
will raise a HelperSubprocessFailed
exception on a non-zero exit, but here we're swallowing virtually any error that could occur and carrying on. The problem with this is it assumes the only thing that could go wrong is grep not finding a match, when there are other legitimate exceptions that could occur that we would want to propagate.
If you find yourself writing rescue StandardError
, it might be a good idea to ask if it's really necessary for what you're trying to do. Doing so will match any subclass of StandardError
, which is the vast majority of errors you'll encounter (the Ruby docs on Exception
are good resource). If you truly want to check for the HelperSubprocessFailed
exception, rescue that class specifically so that other, unexpected exceptions are propagated correctly.
Garry has moved on from the team. The underlying issue still exists, but I suspect whoever tackles this will likely start from a fresh PR, so I'm closing this. Note that there's also this related PR: |
Draft pull request. This is to keep my work in progress. The branch name will change before this PR is finalized. I will be adding code to this as time goes on.